1+ /**
2+ * < 2 neighbors: die
3+ * 2 <= x <= 3: live
4+ * > 3: die
5+ * dead and 3 neighbor -> live
6+ */
7+ const targetElement = document . getElementById ( 'life' )
8+
9+ const PADDING = 5
10+ const DISPLAY_WIDTH = 48
11+ const DISPLAY_HEIGHT = 24
12+ const WIDTH = DISPLAY_WIDTH + 2 * PADDING
13+ const HEIGHT = DISPLAY_HEIGHT + 2 * PADDING
14+
15+ const BLOCK = "#"
16+ const SPACE = "."
17+ const NEWLINE = "<br />"
18+ const DIRECTIONS = [
19+ [ 1 , 0 ] ,
20+ [ - 1 , 0 ] ,
21+ [ 0 , 1 ] ,
22+ [ 0 , - 1 ] ,
23+ [ 1 , 1 ] ,
24+ [ 1 , - 1 ] ,
25+ [ - 1 , 1 ] ,
26+ [ - 1 , - 1 ]
27+ ] ;
28+ let state = Array ( HEIGHT ) . fill ( ) . map ( ( ) => Array ( WIDTH ) . fill ( SPACE ) )
29+ // glider generator
30+ state [ PADDING + 5 ] [ PADDING + 1 ] = BLOCK
31+ state [ PADDING + 5 ] [ PADDING + 2 ] = BLOCK
32+ state [ PADDING + 6 ] [ PADDING + 1 ] = BLOCK
33+ state [ PADDING + 6 ] [ PADDING + 2 ] = BLOCK
34+
35+ state [ PADDING + 3 ] [ PADDING + 13 ] = BLOCK
36+ state [ PADDING + 3 ] [ PADDING + 14 ] = BLOCK
37+ state [ PADDING + 4 ] [ PADDING + 12 ] = BLOCK
38+ state [ PADDING + 5 ] [ PADDING + 11 ] = BLOCK
39+ state [ PADDING + 6 ] [ PADDING + 11 ] = BLOCK
40+ state [ PADDING + 7 ] [ PADDING + 11 ] = BLOCK
41+ state [ PADDING + 8 ] [ PADDING + 12 ] = BLOCK
42+ state [ PADDING + 9 ] [ PADDING + 13 ] = BLOCK
43+ state [ PADDING + 9 ] [ PADDING + 14 ] = BLOCK
44+
45+ state [ PADDING + 6 ] [ PADDING + 15 ] = BLOCK
46+
47+ state [ PADDING + 4 ] [ PADDING + 16 ] = BLOCK
48+ state [ PADDING + 5 ] [ PADDING + 17 ] = BLOCK
49+ state [ PADDING + 6 ] [ PADDING + 17 ] = BLOCK
50+ state [ PADDING + 6 ] [ PADDING + 18 ] = BLOCK
51+ state [ PADDING + 7 ] [ PADDING + 17 ] = BLOCK
52+ state [ PADDING + 8 ] [ PADDING + 16 ] = BLOCK
53+
54+ state [ PADDING + 3 ] [ PADDING + 21 ] = BLOCK
55+ state [ PADDING + 3 ] [ PADDING + 22 ] = BLOCK
56+ state [ PADDING + 4 ] [ PADDING + 21 ] = BLOCK
57+ state [ PADDING + 4 ] [ PADDING + 22 ] = BLOCK
58+ state [ PADDING + 5 ] [ PADDING + 21 ] = BLOCK
59+ state [ PADDING + 5 ] [ PADDING + 22 ] = BLOCK
60+
61+ state [ PADDING + 2 ] [ PADDING + 23 ] = BLOCK
62+ state [ PADDING + 6 ] [ PADDING + 23 ] = BLOCK
63+ state [ PADDING + 1 ] [ PADDING + 25 ] = BLOCK
64+ state [ PADDING + 2 ] [ PADDING + 25 ] = BLOCK
65+ state [ PADDING + 6 ] [ PADDING + 25 ] = BLOCK
66+ state [ PADDING + 7 ] [ PADDING + 25 ] = BLOCK
67+
68+ state [ PADDING + 3 ] [ PADDING + 35 ] = BLOCK
69+ state [ PADDING + 3 ] [ PADDING + 36 ] = BLOCK
70+ state [ PADDING + 4 ] [ PADDING + 35 ] = BLOCK
71+ state [ PADDING + 4 ] [ PADDING + 6 ] = BLOCK
72+
73+ let lifeInterval = setInterval ( ( ) => {
74+ const displayState = state . slice ( PADDING , PADDING + DISPLAY_HEIGHT ) . map ( row =>
75+ row . slice ( PADDING , PADDING + DISPLAY_WIDTH )
76+ )
77+ targetElement . innerHTML = NEWLINE + displayState . map ( row => row . join ( '' ) ) . join ( NEWLINE )
78+ let new_state = Array ( HEIGHT ) . fill ( ) . map ( ( ) => Array ( WIDTH ) . fill ( SPACE ) )
79+ for ( let i = 1 ; i < HEIGHT - 1 ; i += 1 ) {
80+ for ( let j = 1 ; j < WIDTH - 1 ; j += 1 ) {
81+ let alive = state [ i ] [ j ] === BLOCK
82+ let count = DIRECTIONS . reduce ( ( acc , [ di , dj ] ) => {
83+ return acc + ( state [ i + di ] [ j + dj ] === BLOCK ? 1 : 0 )
84+ } , 0 )
85+ new_state [ i ] [ j ] = ( ( alive && ( count === 2 || count === 3 ) ) || ( ! alive && count === 3 ) ) ? BLOCK : SPACE
86+ }
87+ }
88+ state = new_state
89+ } , 100 )
90+
91+ const handleInteraction = ( e ) => {
92+ const rect = targetElement . getBoundingClientRect ( )
93+ const touch = e . touches ? e . touches [ 0 ] : e
94+ const x = touch . clientX - rect . left
95+ const y = touch . clientY - rect . top
96+ const charWidth = rect . width / DISPLAY_WIDTH
97+ const charHeight = rect . height / DISPLAY_HEIGHT
98+
99+ const col = Math . floor ( x / charWidth )
100+ const row = Math . floor ( y / charHeight )
101+
102+ if ( row >= 0 && row < DISPLAY_HEIGHT && col >= 0 && col < DISPLAY_WIDTH ) {
103+ state [ row + PADDING ] [ col + PADDING ] = BLOCK
104+ }
105+ }
106+
107+ targetElement . addEventListener ( 'mousemove' , handleInteraction )
108+ targetElement . addEventListener ( 'touchmove' , handleInteraction )
109+ targetElement . addEventListener ( 'touchstart' , handleInteraction )
110+
111+ window . addEventListener ( 'pagehide' , ( ) => {
112+ clearInterval ( lifeInterval )
113+ } )
0 commit comments