challenge ライフゲーム

セルオートマトンに関するお題です. 
2次元タイプの'ライフゲーム'を実装して下さい. 
初期値としては10行10列程度の格子上の平面に0.3程度の人口(?)密度を考え, 
末端はループするようにして下さい. (例: 座標[-1, -1] = [10, 10])

それだけだと簡単すぎると思われる方は, 
過密状態で間引きが発生するような機能を組み込んで下さい. 
間引きは, 少なくともその後の1時間ステップにおける死亡率が, 
それをしなかった場合よりも小さくなれば結構です. 
(死亡率の最小化は複雑性が高すぎる感がありますし. )
サンプル:
t = 0
[ ][*][ ][ ][ ][ ][*][*][*][ ]
[ ][ ][ ][ ][*][ ][ ][*][*][ ]
[ ][ ][ ][*][ ][ ][*][ ][*][ ]
[*][ ][*][*][ ][ ][*][ ][ ][ ]
[ ][*][ ][ ][ ][ ][ ][ ][*][ ]
[*][ ][ ][ ][*][ ][*][*][ ][*]
[ ][*][ ][ ][ ][ ][*][ ][ ][ ]
[ ][ ][ ][ ][ ][ ][ ][ ][ ][*]
[*][ ][ ][ ][ ][ ][*][ ][ ][*]
[ ][ ][ ][ ][*][*][ ][ ][*][ ]
t = 1
[ ][ ][ ][ ][*][ ][ ][ ][ ][*]
[ ][ ][ ][ ][ ][*][ ][ ][ ][*]
[ ][ ][*][ ][*][*][*][ ][*][*]
[ ][*][ ][*][ ][ ][ ][ ][ ][*]
[ ][ ][*][*][ ][*][*][ ][*][ ]
[ ][*][ ][ ][ ][*][*][ ][*][*]
[ ][ ][ ][ ][ ][*][*][*][*][*]
[ ][ ][ ][ ][ ][ ][ ][ ][ ][*]
[*][ ][ ][ ][ ][*][ ][ ][*][ ]
[*][ ][ ][ ][ ][ ][ ][ ][ ][ ]

Posted feedbacks - Common Lisp

Common Lisp です。あえて CLOS(Common Lisp Object System)で。

print-object で印字形式を設定していること、setf で更新している位であとは普通です。

こんな感じで試します。

cl-user(27): (setq b (make-glider))

#<board

.X........

X.........

XXX.......

..........

..........

..........

..........

..........

..........

..........

>

cl-user(28): (update b)

#<board

..........

X.X.......

XX........

.X........

..........

..........

..........

..........

..........

..........

>

初めてライフゲームを実装して、とても楽しかったです。

 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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
;;;
;; dokaku 126
;;

(defclass board ()
  ((width :accessor board-width :initarg :w)
   (height :accessor board-height :initarg :h)
   (cells :accessor board-cells :initarg :cells)))

(defmethod print-object ((board board) stream)
  (print-unreadable-object (board stream)
    (format stream "board~%")
    (loop for y from 0 below (board-height board)
    do
      (loop for x from 0 below (board-width board)
          do
        (format stream "~a" (if (cell board x y) "X" ".")))
      (format stream "~%"))))

(defun make-board (w h)
  "constructor"
  (make-instance 'board :w w :h h :cells (make-array (list w h) :initial-element nil)))

(defun make-random-board (w h)
  (let ((board (make-board w h)))
    (loop for y from 0 below (board-height board)
    do
      (loop for x from 0 below (board-width board)
          do
        (setf (cell board x y) (< (random 10) 3))))
    board))

(defmethod cell ((board board) x y)
  "accessor"
  (aref (board-cells board) (mod x (board-width board)) (mod y (board-height board))))

;; (setf (cell board x y) value) 
(defsetf cell (board x y) (value)
  `(setf (aref (board-cells ,board) (mod ,x (board-width ,board))
           (mod ,y (board-height ,board))) ,value))

(defmethod cell-neighbours ((board board) x y)
  (list (cell board (1- x) (1- y))
    (cell board x (1- y))
    (cell board (1+ x) (1- y))
    (cell board (1- x) y)
    (cell board (1+ x) y)
    (cell board (1- x) (1+ y))
    (cell board x (1+ y))
    (cell board (1+ x) (1+ y))))

(defmethod cell-survivep ((board board) x y)
  (let ((c (cell board x y))
    (n (count-if #'identity (cell-neighbours board x y))))
    (cond
     ((and (not c) (= n 3)) t) ;; born
     ((and c (or (= n 3) (= n 2))) t) ;; keep
     (t nil)))) ;; die

(defmethod update ((board board))
  (let ((next
     (loop for y from 0 below (board-height board)
         append
           (loop for x from 0 below (board-width board)
           collect (list x y (cell-survivep board x y))))))
    (loop for elt in next
    do
      (destructuring-bind (x y v) elt
        (setf (cell board x y) v))))
  board)

(defun make-blinker ()
  (let ((b (make-board 10 10)))
    (setf (cell b 1 0) t
      (cell b 1 1) t
      (cell b 1 2) t)
    b))

(defun make-glider ()
  (let ((b (make-board 10 10)))
    (setf (cell b 1 0) t
      (cell b 0 1) t
      (cell b 0 2) t
      (cell b 1 2) t
      (cell b 2 2) t)
    b))

大分色物な感じですが、超並列計算機 Connection Machine用のLisp処理系
である*LISP(スターリスプ)のCommon Lisp用のシミュレータパッケージを
使用して書いてみました。
本来、処理をプロセッサノードに割り振って並列に計算するので、今回位の計算
ならば、一度にどかんと計算させて、ループは全く使わないのが*LISPの流儀
だと思うのですが、構文がみつけられなかったため、全く普通に直列な
書き方になっており、あまり意味がないことになっています…。
表示は、グリッドの内容を綺麗に表示するプリティプリンタが付いてくるので、
それを使ってみました。
動作は、AllegroとCLISPで確認しています。
(SBCL等では、ソースを修正しないと*LISPがコンパイルできないようです。)
ソースは参考ページからダウンロード可能でチュートリアル付きです。

;; 実行結果 (グライダー)
     DIMENSION 0 (X)  ----->

0 1 0 0 0 0 0 0 0 0 
1 0 0 0 0 0 0 0 0 0 
1 1 1 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
 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
37
38
39
40
41
42
43
44
45
46
47
48
(defpackage :doukaku-126 (:use :cl :*lisp))
(in-package :doukaku-126)
(*cold-boot :initial-dimensions '(10 10))

(defconstant +alive+ 1)
(defconstant +dead+ 0)

(defun grid10 (x y)
  (grid (mod x 10) (mod y 10)))

(defun get-env (grid x y)
  (values (pref grid (grid x y))
          (count +alive+
                 (list (pref grid (grid10 (1- x) (1- y)))
                       (pref grid (grid10 x (1- y)))
                       (pref grid (grid10 (1+ x) (1- y)))
                       (pref grid (grid10 (1- x) y))
                       (pref grid (grid10 (1+ x) y))
                       (pref grid (grid10 (1- x) (1+ y)))
                       (pref grid (grid10 x (1+ y)))
                       (pref grid (grid10 (1+ x) (1+ y)))))))

(defun gen-next (cur)
  (*let ((next +dead+))
    (loop :for x :from 0 :to 9 
          :do (loop :for y :from 0 :to 9 
                    :do (multiple-value-bind (self env) (get-env cur x y)
                          (cond ((and (eql +dead+ self) (= 3 env)) 
                                 (*setf (pref next (grid x y)) +alive+))
                                ((and (= +alive+ self) (<= 2 env 3))
                                 (*setf (pref next (grid x y)) +alive+))        
                                ('T (*setf (pref next (grid x y)) +dead+))))))
    next))

;; グライダーを作る
(defun make-glider ()
  (*let ((g +dead+))
    (*setf (pref g (grid 1 0)) +alive+
           (pref g (grid 0 1)) +alive+
           (pref g (grid 0 2)) +alive+
           (pref g (grid 1 2)) +alive+
           (pref g (grid 2 2)) +alive+)
    g))

;; 初期値をグライダーにしてループ 
(loop :for gen = (make-glider) then (gen-next gen)
      :do (ppp gen :mode :grid)
      :unless (y-or-n-p) :do (return))

Connection Machine用のLISP、*LISPのCL用シミュレータパッケージを
使用しています。
前回投稿したバージョンは、並列実行の構文を活用できていませんでしたが、
何となく構文が分かって来たので再挑戦してみました。

実行例(初期値をランダムに設定してループ表示):
;     DIMENSION 0 (X)  ----->
;
;0 1 1 1 1 1 1 1 1 1 
;1 0 1 0 1 1 1 0 0 1 
;1 1 0 1 1 1 1 0 0 0 
;0 0 1 1 1 1 0 0 1 0 
;1 1 0 0 1 0 1 1 0 0 
;1 0 1 1 1 0 1 1 0 1 
;1 0 0 0 1 0 1 1 1 1 
;0 1 1 0 0 0 1 1 1 0 
;0 0 0 0 1 1 0 0 0 1 
;0 1 1 1 1 0 0 0 1 0 
;
;     DIMENSION 0 (X)  ----->
;
;0 0 0 0 0 0 0 0 0 0 
;0 0 0 0 0 0 0 0 0 0 
;1 0 0 0 0 0 0 1 0 0 
;0 0 0 0 0 0 0 0 0 1 
;1 0 0 0 0 0 0 0 0 0 
;0 0 1 0 1 0 0 0 0 0 
;0 0 0 0 1 0 0 0 0 0 
;0 1 0 1 1 0 0 0 0 0 
;1 0 0 0 1 1 1 0 0 1 
;0 1 0 0 0 0 0 0 0 0 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
(defpackage :doukaku-126 (:use :cl :*lisp))
(in-package :doukaku-126)
(*cold-boot :initial-dimensions '(10 10))

(defconstant +alive+ 1)
(defconstant +dead+ 0)

(defun gen-next!! (pvar)
  (let ((env (count!! +alive+
              (vector!!
               (news!! pvar -1 -1) (news!! pvar -1  0) (news!! pvar -1  1)
               (news!! pvar  0 -1) (news!! pvar  0  1) 
               (news!! pvar  1 -1) (news!! pvar  1  0) (news!! pvar  1  1)))))
    (cond!! ((and!! (eql!! +dead+ pvar) (=!! env 3)) +alive+)
            ((and!! (eql!! +alive+ pvar) (<=!! 2 env 3)) +alive+)
            (t!! +dead+))))

;; 実行例(初期値をランダムに設定してループ表示):
(loop :for gen = (truncate!! (random!! 10) 5) :then (gen-next!! gen)
      :do (ppp gen) 
      :unless (y-or-n-p) :do (return))

Index

Feed

Other

Link

Pathtraq

loading...