]> gitweb.factorcode.org Git - factor.git/blob - extra/game-of-life/game-of-life.factor
Update actions, because Node.js 16 actions are deprecated, to Node.js 20
[factor.git] / extra / game-of-life / game-of-life.factor
1 ! Copyright (C) 2018 John Benediktsson
2 ! See http://factorcode.org/license.txt for BSD license
3
4 USING: accessors arrays assocs bit-arrays byte-arrays calendar
5 colors combinators kernel kernel.private math
6 math.order ranges namespaces opengl random sequences
7 sequences.private timers ui ui.commands ui.gadgets
8 ui.gadgets.toolbar ui.gadgets.tracks ui.gestures ui.render words ;
9
10 IN: game-of-life
11
12 : make-grid ( rows cols -- grid )
13     '[ _ <bit-array> ] replicate ;
14
15 : grid-dim ( grid -- rows cols )
16     [ length ] [ first length ] bi ;
17
18 : random-grid! ( grid -- )
19     [
20         [ length>> ] [ underlying>> length random-bytes ] bi
21         bit-array boa
22     ] map! drop ;
23
24 :: count-neighbors ( grid -- counts )
25     grid grid-dim { fixnum fixnum } declare :> ( rows cols )
26     rows 1 - { fixnum } declare :> max-rows
27     cols 1 - { fixnum } declare :> max-cols
28     rows [ cols <byte-array> ] replicate :> neighbors
29     grid { array } declare [| row j |
30         j 0 eq? [ max-rows ] [ j 1 - ] if
31         j
32         j max-rows eq? [ 0 ] [ j 1 + ] if
33         [ neighbors nth-unsafe { byte-array } declare ] tri@ :>
34         ( above same below )
35
36         row { bit-array } declare [| cell i |
37             cell [
38                 i 0 eq? [ max-cols ] [ i 1 - ] if
39                 i
40                 i max-cols eq? [ 0 ] [ i 1 + ] if
41
42                 [ [ above [ 1 + ] change-nth-unsafe ] tri@ ]
43                 [ nip [ same [ 1 + ] change-nth-unsafe ] bi@ ]
44                 [ [ below [ 1 + ] change-nth-unsafe ] tri@ ]
45                 3tri
46             ] when
47         ] each-index
48     ] each-index neighbors ;
49
50 :: next-step ( grid -- )
51     grid count-neighbors { array } declare :> neighbors
52     grid { array } declare [| row j |
53         j neighbors nth-unsafe { byte-array } declare :> neighbor-row
54         row { bit-array } declare [| cell i |
55             i neighbor-row nth-unsafe
56             cell [
57                 2 3 between? i row set-nth-unsafe
58             ] [
59                 3 = [ t i row set-nth-unsafe ] when
60             ] if
61         ] each-index
62     ] each-index ;
63
64 TUPLE: grid-gadget < gadget grid size timer ;
65
66 : <grid-gadget> ( grid -- gadget )
67     grid-gadget new
68         swap >>grid
69         20 >>size
70         dup '[ _ [ grid>> next-step ] [ relayout-1 ] bi ]
71         f 1/5 seconds <timer> >>timer ;
72
73 M: grid-gadget ungraft*
74     [ timer>> stop-timer ] [ call-next-method ] bi ;
75
76 M: grid-gadget pref-dim*
77     [ grid>> grid-dim swap ] [ size>> '[ _ * ] bi@ 1 + 2array ] bi ;
78
79 :: update-grid ( gadget -- )
80     gadget dim>> first2 :> ( w h )
81     gadget size>> :> size
82     h w [ size /i ] bi@ :> ( new-rows new-cols )
83     gadget grid>> :> grid
84     grid grid-dim :> ( rows cols )
85     rows new-rows = not
86     cols new-cols = not or [
87         new-rows new-cols make-grid :> new-grid
88         rows new-rows min <iota> [| j |
89             cols new-cols min <iota> [| i |
90                 i j grid nth nth
91                 i j new-grid nth set-nth
92             ] each
93         ] each
94         new-grid gadget grid<<
95     ] when ;
96
97 :: draw-cells ( gadget -- )
98     COLOR: black gl-color
99     gadget size>> :> size
100     gadget grid>> { array } declare [| row j |
101         row { bit-array } declare [| cell i |
102             cell [
103                 i j [ size * ] bi@ 2array
104                 { size size } gl-fill-rect
105             ] when
106         ] each-index
107     ] each-index ;
108
109 :: draw-lines ( gadget -- )
110     gadget size>> :> size
111     gadget grid>> grid-dim :> ( rows cols )
112     COLOR: gray gl-color
113     cols rows [ size * ] bi@ :> ( w h )
114     rows [0..b] [| j |
115         j size * :> y
116         { 0 y } { w y } gl-line
117         cols [0..b] [| i |
118             i size * :> x
119             { x 0 } { x h } gl-line
120         ] each
121     ] each ;
122
123 M: grid-gadget draw-gadget*
124     [ update-grid ] [ draw-cells ] [ draw-lines ] tri ;
125
126 SYMBOL: last-click
127
128 :: on-click ( gadget -- )
129     gadget size>> :> size
130     gadget grid>> grid-dim :> ( rows cols )
131     gadget hand-rel first2 [ size /i ] bi@ :> ( i j )
132     i 0 cols 1 - between?
133     j 0 rows 1 - between? and [
134         i j gadget grid>> nth
135         [ not dup last-click set ] change-nth
136     ] when gadget relayout-1 ;
137
138 :: on-drag ( gadget -- )
139     gadget size>> :> size
140     gadget grid>> grid-dim :> ( rows cols )
141     gadget hand-rel first2 [ size /i ] bi@ :> ( i j )
142     i 0 cols 1 - between?
143     j 0 rows 1 - between? and [
144         last-click get i j gadget grid>> nth set-nth
145     ] when gadget relayout-1 ;
146
147 : on-scroll ( gadget -- )
148     [
149         scroll-direction get second {
150             { [ dup 0 > ] [ -2 ] }
151             { [ dup 0 < ] [ 2 ] }
152             [ 0 ]
153         } cond nip + 4 30 clamp
154     ] change-size relayout-1 ;
155
156 :: com-play ( gadget -- )
157     gadget timer>> restart-timer ;
158
159 :: com-step ( gadget -- )
160     gadget grid>> next-step
161     gadget relayout-1 ;
162
163 :: com-stop ( gadget -- )
164     gadget timer>> stop-timer ;
165
166 :: com-clear ( gadget -- )
167     gadget grid>> [ clear-bits ] each
168     gadget relayout-1 ;
169
170 :: com-random ( gadget -- )
171     gadget grid>> random-grid! gadget relayout-1 ;
172
173 :: com-glider ( gadget -- )
174     gadget grid>> :> grid
175     { { 2 1 } { 3 2 } { 1 3 } { 2 3 } { 3 3 } }
176     [ first2 grid nth t -rot set-nth ] each
177     gadget relayout-1 ;
178
179 grid-gadget "toolbar" f {
180     { T{ key-down { sym "1" } } com-play }
181     { T{ key-down { sym "2" } } com-stop }
182     { T{ key-down { sym "3" } } com-clear }
183     { T{ key-down { sym "4" } } com-random }
184     { T{ key-down { sym "5" } } com-glider }
185     { T{ key-down { sym "6" } } com-step }
186 } define-command-map
187
188 grid-gadget "gestures" [
189     {
190         { T{ key-down f { A+ } "F" } [ toggle-fullscreen ] }
191         { T{ button-down { # 1 } } [ on-click ] }
192         { T{ drag { # 1 } } [ on-drag ] }
193         { mouse-scroll [ on-scroll ] }
194     } assoc-union
195 ] change-word-prop
196
197 TUPLE: life-gadget < track ;
198
199 : <life-gadget> ( -- gadget )
200     vertical life-gadget new-track
201     20 20 make-grid <grid-gadget>
202     [ <toolbar> format-toolbar f track-add ]
203     [ 1 track-add ] bi ;
204
205 M: life-gadget focusable-child* children>> second ;
206
207 MAIN-WINDOW: life-window {
208         { title "Game of Life" }
209     } <life-gadget> >>gadgets ;