challenge マルバツゲーム

マルバツゲームは3×3の格子に交互に○と×を書き込み、先に縦・横・斜めに記号をそろえたほうが勝ちというおなじみのゲームです。

「毎ターン乱数を使って手を決めるランダムプレイヤー同士を対戦させる」というのが今回のお題です。 1万回対戦させ、勝ち・負け・引き分けの数を表示してください。 そして先手が有利であることを確かめてください。

良い手を思考するプレイヤーについては別のお題にしようと思っています。 プレイヤーを簡単に差し換えることができる設計を目指してください。

Posted feedbacks - Common Lisp

勝利条件の判定をきれいに書けないかなってことばかり考えてました。で揃ってる並びって等差数列だなとか思って、真ん中の値と可能な公差の連想リストで表現してみました。

他はわりと適当です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
(defconstant +diff-alist+ '((1 1) (3 3) (4 1 2 3 4) (5 3) (7 1)))
(defun wonp (ps)
  (loop for (m . ds) in +diff-alist+
    if (member m ps)
    do (loop for d in ds
         if (and (member (- m d) ps) (member (+ m d) ps))
         do (return-from wonp t))))

(defun execute-game (player-O player-X)
  (do ((vacant (list 0 1 2 3 4 5 6 7 8))
       (turn :O (if (eq turn :O) :X :O))
       (pl1 player-O pl2)
       (pl2 player-X pl1)
       (ps1 () ps2)
       (ps2 () ps1))
      ((null vacant) :draw)
    (multiple-value-setq (ps1 vacant) (funcall pl1 ps1 ps2 vacant))
    (if (wonp ps1) (return turn))))

(let ((state (make-random-state t)))
  (defun random-player (ps-player ps-opponent vacant)
    (declare (ignore ps-opponent))
    (let ((r (random (length vacant) state)))
      (if (zerop r)
          (values (cons (car vacant) ps-player) (cdr vacant))
        (let ((p (pop (cdr (nthcdr (1- r) vacant)))))
          (values (cons p ps-player) vacant))))))

;;; test
(let ((a 0) (b 0) (c 0))
  (dotimes (i 10000)
    (case (execute-game #'random-player #'random-player)
      (:O (incf a))
      (:X (incf b))
      (:draw (incf c))))
  (format t "O win: ~A~%X win: ~A~%draw: ~A" a b c))

Index

Feed

Other

Link

Pathtraq

loading...