Advertisement
Guest User

Untitled

a guest
Dec 29th, 2018
131
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Racket 19.51 KB | None | 0 0
  1. #lang slideshow
  2. (require racket/class
  3.          racket/set
  4.         lazy/force
  5.          racket/promise
  6.          compatibility/mlist
  7.          (only-in racket/list shuffle argmax))
  8.  
  9. ;;--------------------------------------------------------------------
  10. ;; Реализация минимакса с альфа-бета отсечением
  11. (define (minimax tree)
  12.   (define (minimax-h node alpha beta max-player)
  13.     (define (next-max x v)
  14.       (if (or (null? x) (<= beta v))
  15.                 v
  16.                 (next-max (cdr x)
  17.                       (max v (minimax-h (car x) v beta (not max-player)))))
  18.     )
  19.     (define (next-min x v)
  20.       (if (or (null? x) (<= v alpha))
  21.                 v
  22.                 (next-min (cdr x)
  23.                       (min v (minimax-h (car x) alpha v (not max-player)))))
  24.     )
  25.     (cond
  26.          ((number? node) node)
  27.          ((null? node) 0.0)
  28.          (max-player (next-max node alpha))
  29.          (else (next-min node beta)))
  30.          
  31.   )
  32.   (!(minimax-h tree -inf.0 +inf.0 #f))
  33.  )
  34.  
  35.  
  36. ;;--------------------------------------------------------------------
  37. ;; описание класса, реализующего логику и оптимальную стратегию произвольной игры с нулевой суммой и полной информацией
  38. (define game%
  39.   (class object%
  40.     (super-new)
  41.  
  42.     ;; виртуальные методы для задания правил игры
  43.     (init-field my-win?         ; State -> Bool
  44.                 my-loss?        ; State -> Bool
  45.                 draw-game?      ; State -> Bool
  46.                 my-move         ; State Move -> State
  47.                 opponent-move   ; State Move -> State
  48.                 show-state      ; State -> Any
  49.                 possible-moves  ; State -> (list Move)
  50.                 [number 'undefined]
  51.                 )
  52.  
  53.     ;; optimal-move :: State -> Move
  54.     ;; выбор оптимального хода по минимаксу
  55.     ;; из нескольких оптимальных выбирается один случайно
  56.     (define/public ((optimal-move look-ahead) S)
  57.       (!(argmax (lambda (m) (!(minimax (game-tree S m look-ahead))))
  58.                  (shuffle (possible-moves S number)))))
  59.  
  60.     ;; game-tree :: State -> (Move -> (Tree of Real))
  61.     ;; построение дерева с оценками
  62.     (define (game-tree St m look-ahead)
  63.        ;; вспомогательная функция, строящая закольцованный список из пары элементов
  64.       (define (help a b) (begin (define l (mlist a b a)) (set-mcdr! l (mcons b l)) l))
  65.       (define (new-ply moves i s)    
  66.         (cond
  67.           ((my-win? s) +inf.0) ; в выигрышной позиции оценка = + бесконечность
  68.           ((my-loss? s) -inf.0) ; в проигрышной = - бесконечность
  69.           ((draw-game? s)     0) ; при ничье = 0
  70.           ((>= i look-ahead)  (f-h s)) ; если исчерпана глубина, то используется эвристическая оценка
  71.           (else (map (lambda (x) (new-ply (mcdr moves) (+ 1 i) ((mcar moves) s x)))
  72.                      (possible-moves s number))) ; рассматриваем все возможные ходы и строим их оценки
  73.         ))
  74.      (new-ply (help opponent-move my-move) 1 (my-move St m))
  75.     )
  76.  
  77.     ;; make-move :: State (State -> Move) -> (Move State Symbol)
  78.     (define/public (make-move S move)
  79.       (cond
  80.         ((my-loss? S)   (values '() S 'loss))        
  81.         ((draw-game? S) (values '() S 'draw))  
  82.         (else (let* ((m* (! (move S)))
  83.                      (S* (my-move S m*)))
  84.                 (cond
  85.                   ((my-win? S*)    (values m* S* 'win))
  86.                   ((draw-game? S*) (values m* S* 'draw))
  87.                   (else            (values m* S* 'next))))))
  88.     )
  89.   ))
  90.  
  91. ;;--------------------------------------------------------------------
  92. ;; Реализация класса игрока
  93. ;; Параметр `game` указывает, в какая игра решается.
  94. (define (interactive-player game)
  95.   (class game
  96.     (super-new)
  97.  
  98.     (inherit-field show-state)
  99.     (inherit make-move optimal-move)
  100.  
  101.     (init-field name
  102.                 [look-ahead 4]
  103.                 [opponent 'undefined]
  104.                 [move-method (optimal-move look-ahead)])
  105.  
  106.     (define/public (your-turn S)
  107.       (define-values (m S* status) (make-move S move-method))
  108.       (! (printf "\n~a makes move ~a\n" name m))
  109.       (! (show-state S*))
  110.       (! (case status
  111.            ['stop (displayln "The game was interrupted.")]
  112.            ['win  (printf "~a wins!" name)]
  113.            ['loss (printf "~a wins!" name)]
  114.            ['draw (printf "Draw!")]
  115.            [else (send opponent your-turn S*)])))))
  116.  
  117.  
  118. ;;--------------------------------------------------------------------
  119. ;; макрос для описания партнеров в игре
  120. (define-syntax-rule
  121.   (define-partners game (A #:win A-wins #:move A-move #:number a-number)
  122.                         (B #:win B-wins #:move B-move #:number b-number))
  123.   (begin
  124.     (define A (class game
  125.                 (super-new
  126.                  [my-win?  A-wins]
  127.                  [my-loss? B-wins]
  128.                  [my-move  A-move]
  129.                  [opponent-move B-move]
  130.                  [number a-number])))
  131.     (define B (class game
  132.                 (super-new
  133.                  [my-win?  B-wins]
  134.                  [my-loss? A-wins]
  135.                  [my-move  B-move]
  136.                  [opponent-move A-move]
  137.                  [number b-number])))))
  138.  
  139.  
  140. ;;--------------------------------------------------------------------
  141. ;; Описания крестиков-ноликов
  142.  
  143. ;; Структура представляющая ситуацию на игровой доске. В структуре три поля -- список точек фигуры крсного игрока, список точек фигуры синего игрока, две точки черных фишек
  144. (struct board (l1 l2 os))
  145.  
  146. ;; геттеры полей структуры
  147. (define l1 board-l1)
  148. (define l2 board-l2)
  149. (define os board-os)
  150.  
  151. ;; начальная игровая доска
  152. (define empty-board (board (list 1 2 6 10) (list 5 9 13 14) (list 0 15)))
  153.  
  154.  
  155. ; move - starting_point rotation variation new_blacks
  156.  
  157. ; получить список точек фигуры игрока после применения хода, если при применении такого хода фигуры выходит за пределы игрового поля,
  158. ; то тем частям, которые вышли за пределы поля присваивается -1
  159. (define (l-points-for-move move)
  160.   ; вспомогательные функции - возвращают номер соседней клетки или -1, если она за пределами игрового поля
  161.   (define (up point)
  162.     (if (< point 4)
  163.         -1
  164.         (- point 4)))
  165.   (define (down point)
  166.     (if (> point 11)
  167.         -1
  168.         (+ point 4)))
  169.   (define (left point)
  170.     (if (< (remainder point 4) 1)
  171.         -1
  172.         (- point 1)))
  173.   (define (right point)
  174.     (if (> (remainder point 4) 2)
  175.         -1
  176.         (+ point 1)))
  177.   (let ((base (car move)))
  178.           (cond
  179.             ((and (= (cadr move) 0) (= (caddr move) 0)) (list base (up base) (up (up base)) (right base)))
  180.             ((and (= (cadr move) 0) (= (caddr move) 1)) (list base (up base) (right (right base)) (right base)))
  181.            
  182.             ((and (= (cadr move) 1) (= (caddr move) 0)) (list base (right base) (down base) (right (right base))))
  183.             ((and (= (cadr move) 1) (= (caddr move) 1)) (list base (right base) (down base) (down (down base))))
  184.            
  185.             ((and (= (cadr move) 2) (= (caddr move) 0)) (list base (down base) (left base) (down (down base))))
  186.             ((and (= (cadr move) 2) (= (caddr move) 1)) (list base (down base) (left base) (left (left base))))
  187.            
  188.             ((and (= (cadr move) 3) (= (caddr move) 0)) (list base (left base) (up base) (left (left base))))
  189.             ((and (= (cadr move) 3) (= (caddr move) 1)) (list base (left base) (up base) (up (up base)))))))
  190.  
  191. ; проверка на то, что фигура находится целиком на игровом поле
  192. (define (l-points-valid? points)
  193.   (andmap (lambda (x) (>= x 0)) points))
  194.  
  195. ; проверка на то, что данный ход является допустимым
  196. (define (valid-move? b move player-num)
  197.   (let* ((l-points (l-points-for-move move)) ; точки, которые займет фигура игрока после перемещения
  198.          (new-blacks (cadddr move))) ; новые точки черных фигур
  199.    
  200.     (and
  201.          ; не вышло за границу игрового поля
  202.          (l-points-valid? l-points)
  203.  
  204.          ; две черных фигуры не слились в одну
  205.          (not (= (car new-blacks) (cadr new-blacks)))
  206.  
  207.          ; новая позиция фигуры игрока не пересекается с фигурой противника
  208.          (cond ((= player-num 1) (andmap (lambda (x) (not (member x (l2 b)))) l-points))
  209.                ((= player-num 2) (andmap (lambda (x) (not (member x (l1 b)))) l-points)))
  210.  
  211.          ; новая позиция фигуры игрока хотя бы на одну клетку отличается от старой
  212.          (cond ((= player-num 1) (ormap (lambda (x) (not (member x (l1 b)))) l-points))
  213.                ((= player-num 2) (ormap (lambda (x) (not (member x (l2 b)))) l-points)))
  214.  
  215.          ; новая позиция фигуры игрока не пересекается с черными фигурами
  216.          (andmap (lambda (x) (not (member x (os b)))) l-points)
  217.  
  218.          ; только одна из черных фигур поменяла позицию
  219.          (or (= (car new-blacks) (car (os b)))
  220.              (= (cadr new-blacks) (car (os b)))
  221.              (= (car new-blacks) (cadr (os b)))
  222.              (= (cadr new-blacks) (cadr (os b))))
  223.  
  224.          ; новые позиции черных фигур не пересекаются с фигурами игроков
  225.          (cond ((= player-num 1) (andmap (lambda (x) (and (not (member x (l2 b))) (not (member x l-points)))) new-blacks))
  226.                ((= player-num 2) (andmap (lambda (x) (and (not (member x (l1 b))) (not (member x l-points)))) new-blacks)))
  227.          
  228.          )))
  229.  
  230. ; сделать ход
  231. (define (make-move b move player-num)
  232.   (let ((l-points (l-points-for-move move)))
  233.     (cond ((= player-num 1) (board l-points (l2 b) (cadddr move)))
  234.           ((= player-num 2) (board (l1 b) l-points (cadddr move))))))
  235.  
  236. ; вспомогательная функция, возвращает список точек игрового поля, которые не принадлежат игроку с указанным номером
  237. (define (points-not-under-number b number)
  238.   (filter (lambda (x) (and (not (member x (os b)))
  239.                            (cond
  240.                              ((= number 1) (not (member x (l2 b))))
  241.                              ((= number 2) (not (member x (l1 b))))
  242.                              ))) (build-list 16 (lambda (x) x))))
  243. ; вспомогательная функция, возвращает список точек игрового поля, на которых нет черных фигур
  244. (define (points-not-under-black b)
  245.   (filter (lambda (x) (and (not (member x (os b))))) (build-list 16 (lambda (x) x))))
  246.  
  247.  
  248. ;; проверка, является ли ситуация на игровой доске выигрышной
  249. ; ситуация является выигрышной только в одном случае - противник не может сделать хода своей фигурой
  250. ; эта функция для каждой открытой клетки пытается на ней разместить угол фигуры противника, а затем проверяет, является ли это легальным ходом
  251. ; как только хотя бы один такой ход найден - значит ситуация не является выигрышной - перебор прекращается
  252. (define ((wins? player-num) b)
  253.   (let* ((open-points (points-not-under-number b (if (= player-num 1) 2 1)))
  254.          (possible-moves (cartesian-product open-points (list 0 1 2 3) (list 0 1) (list (os b))))
  255.          (opponent-num (if (= player-num 1) 2 1)))
  256.     (not (ormap (lambda (x) (valid-move? b x opponent-num)) possible-moves))
  257.     ))
  258.  
  259.  
  260. ; получить список всех легальных ходов для игрока с указанным номером
  261. ; сначала происходит перебор всех возможных ходов фигурой игрока, отбираются только легальные
  262. ; затем для каждого найденного хода фигурой добавляются всевозможные ходы черной фигурой (в том числе бездействие)
  263. (define (valid-moves b player-num)
  264.   (let* ((open-points (points-not-under-number b player-num))
  265.          (all-points (build-list 16 (lambda (x) x)))
  266.          (possible-moves (cartesian-product open-points (list 0 1 2 3) (list 0 1) (list (os b))))
  267.          (possible-l-moves (filter (lambda (x) (valid-move? b x player-num)) possible-moves)))
  268.  
  269.     (filter (lambda (x) (valid-move? b x player-num))
  270.             (append possible-l-moves
  271.                     (map (lambda (x) (list (car (car x)) (cadr (car x)) (caddr (car x)) (list (cadr x) (cadr (cadddr (car x)))) )) (cartesian-product possible-l-moves open-points))
  272.                     (map (lambda (x) (list (car (car x)) (cadr (car x)) (caddr (car x)) (list (car (cadddr (car x))) (cadr x)) )) (cartesian-product possible-l-moves open-points))))))
  273.  
  274. ;; функция эвристической оценки позиции
  275. ; поскольку считать количество ходов получается очень долго - функция такая
  276. ; если открыть страницу этой игры в википедии, то можно заметить, что в 12 из 15 позиций, где игрок проигрывает,
  277. ; его фигура находится в углу
  278. (define (f-h b)
  279.  
  280.     (+ (if (or
  281.          (and (member 0 (l2 b)) (member 1 (l2 b)) (member 4 (l2 b)))
  282.          (and (member 2 (l2 b)) (member 3 (l2 b)) (member 7 (l2 b)))
  283.          (and (member 15 (l2 b)) (member 14 (l2 b)) (member 11 (l2 b)))
  284.          (and (member 8 (l2 b)) (member 12 (l2 b)) (member 13 (l2 b))))
  285.         -100
  286.         0)
  287.        (if (or
  288.          (and (member 0 (l1 b)) (member 1 (l1 b)) (member 4 (l1 b)))
  289.          (and (member 2 (l1 b)) (member 3 (l1 b)) (member 7 (l1 b)))
  290.          (and (member 15 (l1 b)) (member 14 (l1 b)) (member 11 (l1 b)))
  291.          (and (member 8 (l1 b)) (member 12 (l1 b)) (member 13 (l1 b))))
  292.         100
  293.         0
  294.         )))
  295.  
  296. ;; функции для осуществления ходов игроков
  297. (define (red-move b m)  (make-move b m 1))
  298. (define (blue-move b m) (make-move b m 2))
  299.  
  300. ;; вывод игровой доски
  301. (define (show-board b)
  302.   (for ([i (list 0 1 2 3)])
  303.     (for ([j (list 0 1 2 3)])
  304.       (display (~r (+ (* i 4) j) #:min-width 2 #:pad-string " ") )
  305.  
  306.       (print (colorize (filled-rectangle 40 40) (cond
  307.                                                   ((member (+ (* i 4) j) (l1 b)) "red")
  308.                                                   ((member (+ (* i 4) j) (l2 b)) "blue")
  309.                                                   ((member (+ (* i 4) j) (os b)) "black")
  310.                                                   (else "gray"))))
  311.       )
  312.     (display "\n")))
  313.  
  314. ;(set! empty-board (board (list 12 13 14 8) (list 2 6 10 9) (list 4 3)))
  315. ;(show-board empty-board)
  316. ;((wins? 2) empty-board)
  317. ;(valid-move? empty-board (list 3 2 0 (list 0 12)) 2)
  318. ;(show-board (make-move empty-board (list 3 2 0 (list 0 12)) 2))
  319.  
  320. ;;--------------------------------------------------------------------
  321. ;; Описание класса-игры крестики-нолики
  322. (define lgame%
  323.   (class game%
  324.     (super-new
  325.      [draw-game?       (lambda (x) #f)]
  326.      [show-state       show-board]
  327.      [possible-moves valid-moves])))
  328.  
  329. ;; описания партнеров для крестиков-ноликов
  330. (define-partners lgame%
  331.   (red% #:win (wins? 1) #:move red-move #:number 1)
  332.   (blue% #:win (wins? 2) #:move blue-move #:number 2))
  333.  
  334. ;;; объекты-игроки ИИ
  335. (define player-A (new (force (interactive-player red%)) [name "Red"] [look-ahead 3] ))
  336.  
  337. (define player-B (new (force (interactive-player blue%)) [name "Blue"] [look-ahead 3]))
  338.  
  339. ;; функция получения хода
  340. (define (input-move b  player-num)
  341.   (printf "Plese enter your move like that (base rotation variation black1 black2): ")
  342.   (let ((m (read)))
  343.   (cond ((or (not (pair? m)) (not (= (length m) 5))) (input-move b player-num))
  344.         (else
  345.   (let ((move (list (car m) (cadr m) (caddr m) (list (cadddr m) (cadddr (cdr m))))))
  346.   (cond ((valid-move? b move player-num) move)
  347.         (else (printf "Invalid move\n")  (input-move b  player-num))))))
  348. ))
  349.  
  350. (define user-A
  351.   (new (force (interactive-player red%))
  352.        [name "Red Player"]
  353.        [move-method
  354.         (lambda (b) (input-move b  1))]
  355.         )
  356.  )
  357. (define user-B
  358.   (new (force (interactive-player blue%))
  359.        [name "Blue Player"]
  360.        [move-method
  361.         (lambda (b) (input-move b 2))]
  362.         )
  363.  )
  364.  
  365.  
  366. ;;--------------------------------------------------------------------
  367. ;; функция, инициализирующая игру
  368. (define (start-game p1 p2 initial-state)
  369.   (printf "Каждая клета игрового поля имеет номер от 0 до 15\n")
  370.   (printf "Первым ходит красный игрок\n")
  371.   (printf "Ход игрока имеет следующий вид: (база поворот тип черная1 черная2)\n")
  372.   (printf "база - номер клетки, где должен находиться угол фигуры после хода\n")
  373.   (printf "поворот - (принимает значения 0 1 2 3), 0 - у базовой клетки есть соседи сверху и справа, 1 - справа и снизу, 2 - низ и лево, 3 - лево и верх\n")
  374.   (printf "    (базовая клетка - это угол фигуры)\n")
  375.   (printf "тип - принимает значения 0 и 1,\n")
  376.   (show-board initial-state)
  377.   (set-field! opponent p1 p2)
  378.   (set-field! opponent p2 p1)
  379.   (send p1 your-turn initial-state))
  380.  
  381. (define (start)
  382.   (printf "Choose game mode (1, 2 or 3):\n1. Human vs Human\n2. AI vs AI\n3.Human vs AI\n")
  383.   (let ((choice (read)))
  384.     (cond
  385.       ((and (integer? choice) (= choice 1)) (!(start-game user-A user-B empty-board)))
  386.       ((and (integer? choice) (= choice 2)) (!(start-game player-A player-B empty-board)))
  387.       ((and (integer? choice) (= choice 3)) (!(start-game user-A player-B empty-board)))
  388.       (else (start )))))
  389. (start)
  390. ;(show-board empty-board)
  391. ;(!(start-game player-A player-B empty-board))
  392.  
  393. ;(define testboard (board (list 1 2 3 7) (list 6 8 9 10) (list 0 15)))
  394. ;(show-board testboard)
  395. ;(valid-moves testboard 1)
  396. ;((wins? 2) testboard)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement